package com.snts.synchronization.core;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

public class Change {

	public enum Type {UPDATE, INSERT, DELETE}
	
	private UUID id;    // identifier of change in log table
	private UUID rowId; // identifier of table row (primary key)
	private UUID sourceRestaurantId; // The restaurant 
	private SyncTable table;
	private List<ChangeColumn> columns = new ArrayList<ChangeColumn>();
	private TimeStampWithTimeZone time;
	private UUID transactionId;
	private Type type;
	private UUID restaurantId;

	public Change(Type type) {
		super();
		this.type = type;
	}

	public UUID getId() {
		return id;
	}

	public void setId(UUID id) {
		this.id = id;
	}

	public UUID getRowId() {
		return rowId;
	}

	public void setRowId(UUID rowId) {
		this.rowId = rowId;
	}

	public UUID getSourceRestaurantId() {
		return sourceRestaurantId;
	}

	public void setSourceRestaurantId(UUID sourceRestaurantId) {
		this.sourceRestaurantId = sourceRestaurantId;
	}

	public SyncTable getTable() {
		return table;
	}

	public void setTable(SyncTable table) {
		this.table = table;
	}

	public List<ChangeColumn> getColumns() {
		return columns;
	}

	public ChangeColumn addColumn(Column column){
		if(table == null){
			throw new IllegalStateException("table mustn't be null");
		}
		if(!table.equals(column.getTable())){
			throw new IllegalArgumentException("table must be the same");
		}
		for(ChangeColumn chc : getColumns()){
			if(column.equals(chc.column)){
				throw new IllegalArgumentException("column \"" + column.getName() + "\" already in list");
			}
		}
		ChangeColumn changeColumn = new ChangeColumn(column);
		columns.add(changeColumn);
		return changeColumn;
	}

	public UUID getRestaurantId() {
		return restaurantId;
	}

	public void setRestaurantId(UUID restaurantId) {
		this.restaurantId = restaurantId;
	}

	public void setType(Type type) {
		this.type = type;
	}

	public Object[] getValues() {
		Object[] values = new String[columns.size()];
		int index = 0;
		for(ChangeColumn chc : columns){
			values[index++] = chc.getValue();
		}
		return values;
	}

	public Object getValue(int index){
		return columns.get(index).getValue();
	}

	public void setValues(Object[] values) {
		if(values.length != columns.size()){
			throw new IllegalArgumentException("values must be of the same size as columns");
		}
		int index = 0;
		for(ChangeColumn chc : columns){
			chc.setValue(values[index++]);
		}		
	}

	public TimeStampWithTimeZone getTime() {
		return time;
	}

	public void setTime(TimeStampWithTimeZone time) {
		this.time = time;
	}

	public UUID getTransactionId() {
		return transactionId;
	}

	public void setTransactionId(UUID transactionId) {
		this.transactionId = transactionId;
	}

	public Type getType() {
		return type;
	}

	public UUID getRestaurantId(Connection conn){
		if(restaurantId == null){
			try {
				PreparedStatement pstmt = conn.prepareStatement(getTable().getJoinRoute().buildRestaurantIdQuery());
				try {
					pstmt.setString(1, rowId.toString());
					ResultSet rs = pstmt.executeQuery();
					try {
						if(rs.next())
							restaurantId = new UUID(rs.getString(1));
					} finally {
						rs.close();
					}
				} finally {
					pstmt.close();
				}
			} catch (SQLException e) {
				throw new RuntimeException("error obtaining restaurantId for change[" + id + "]", e);
			}
		}
		return restaurantId;
	}

	public ChangeColumn getColumnByName(String columnName){
		if(columnName == null){
			throw new IllegalArgumentException("columnName mustn't be null");
		}
		for(ChangeColumn column : columns){
			if(columnName.equals(column.getColumn().getName())){
				return column;
			}
		}
		return null;
	}

	@Override
	public String toString() {
		return "Change [id=" + id + ", rowId=" + rowId + ", sourceRestaurantId=" + sourceRestaurantId + ", table=" + table + ", columns="
				+ toString(columns) + ", time=" + time + ", transactionId=" + transactionId + ", type=" + type + ", restaurantId=" + restaurantId
				+ "]";
	}

	private String toString(List<ChangeColumn> columns2) {
		StringBuffer buf = new StringBuffer("[");
		for(ChangeColumn chc : columns){
			buf.append(chc.toString() + ",\n");
		}
		buf.append("]");
		return buf.toString();
	}

}
